package semantic.analyzer;

import java.util.List;

import metamodel.PrimitiveTypeEnum;
import semantic.SemanticException;
import semantic.model.ExpressionNode;
import semantic.model.PropertyCallNode;

public class CollectionAnalyzer {

	private static final String SIZE_OP = "size";
	private static final String INCLUDES_OP = "includes";
	private static final String EXCLUDES_OP = "excludes";
	private static final String SELECT_OP = "select";
	private static final String EXISTS_OP = "exists";
	private static final String FORALL_OP = "forAll";
	
	private static String[] collections = new String[] {"Bag", "Sequence", "Set"};
	
	public static void checkIfItsCollection(String type) {
		for (int i = 0; i < collections.length; i++) {
			if (type.startsWith(collections[i])) {
				return;
			}
		}
		throw new SemanticException("Era esperado um tipo de coleção, mas foi '" + type + "'");
	}

	public static String getType(String type, PropertyCallNode tempPropertyCallNode) {
		if (tempPropertyCallNode.getPath().equals(SIZE_OP)) {
			return PrimitiveTypeEnum.REAL.name();
		} else {
			if (tempPropertyCallNode.getPath().equals(SELECT_OP) || tempPropertyCallNode.getPath().equals(FORALL_OP) || tempPropertyCallNode.getPath().equals(EXISTS_OP)) {
				String insideType = tempPropertyCallNode.getParameterList().get(0).getType();
				checkParametersLength(tempPropertyCallNode.getParameterList());
				if (!insideType.equalsIgnoreCase(PrimitiveTypeEnum.BOOLEAN.name())) {
					throw new SemanticException("Era esperado parâmetro do tipo Boolean para a operação '" + tempPropertyCallNode.getPath() + "'");
				}
			} else {
				String collectionType = extractInsideType(type);
				String insideType = tempPropertyCallNode.getParameterList().get(0).getType();
				checkParametersLength(tempPropertyCallNode.getParameterList());
				if (!insideType.equalsIgnoreCase(collectionType)) {
					throw new SemanticException("Era esperado o tipo '" + collectionType + "' ao invés de '" + insideType + "' na operação '" + tempPropertyCallNode.getPath() + "'");
				}
			}

			if (tempPropertyCallNode.getPath().equals(EXCLUDES_OP) || tempPropertyCallNode.getPath().equals(INCLUDES_OP)) {
				return PrimitiveTypeEnum.BOOLEAN.name();
			}
			return type;
		}
	}

	private static void checkParametersLength(List<ExpressionNode> parameterList) {
		if (parameterList.size() != 1) {
			throw new SemanticException("Era esperado apenas um parâmetro.");
		}
	}

	private static String extractInsideType(String type) {
		return type.substring(type.indexOf("<") + 1, type.indexOf(">"));
	}

}
